package com.cloud.rou.im.server;

import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.SimpleTimeLimiter;
import com.google.common.util.concurrent.TimeLimiter;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Factory class for instances of {@link AttemptTimeLimiter}
 */
public class AttemptTimeLimiters {
    private AttemptTimeLimiters() {
    }

    public static <V> AttemptTimeLimiter<V> noTimeLimit() {
        return new AttemptTimeLimiters.NoAttemptTimeLimit();
    }

    public static <V> AttemptTimeLimiter<V> fixedTimeLimit(long duration, TimeUnit timeUnit) {
        Preconditions.checkNotNull(timeUnit);
        return new AttemptTimeLimiters.FixedAttemptTimeLimit(duration, timeUnit);
    }

    public static <V> AttemptTimeLimiter<V> fixedTimeLimit(long duration,TimeUnit timeUnit,  ExecutorService executorService) {
        Preconditions.checkNotNull(timeUnit);
        return new AttemptTimeLimiters.FixedAttemptTimeLimit(duration, timeUnit, executorService);
    }

    private static final class FixedAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {
        private final TimeLimiter timeLimiter;
        private final long duration;
        private final TimeUnit timeUnit;

        public FixedAttemptTimeLimit(long duration, TimeUnit timeUnit) {
            this(new SimpleTimeLimiter(), duration, timeUnit);
        }

        public FixedAttemptTimeLimit(long duration, TimeUnit timeUnit,  ExecutorService executorService) {
            this(new SimpleTimeLimiter(executorService), duration, timeUnit);
        }

        private FixedAttemptTimeLimit( TimeLimiter timeLimiter, long duration,  TimeUnit timeUnit) {
            Preconditions.checkNotNull(timeLimiter);
            Preconditions.checkNotNull(timeUnit);
            this.timeLimiter = timeLimiter;
            this.duration = duration;
            this.timeUnit = timeUnit;
        }
        @Override
        public V call(Callable<V> callable) throws Exception {
            return this.timeLimiter.callWithTimeout(callable, this.duration, this.timeUnit, true);
        }
    }

    private static final class NoAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {
        private NoAttemptTimeLimit() {
        }

        public V call(Callable<V> callable) throws Exception {
            return callable.call();
        }
    }
}
